﻿using System.Data;
using Benefactor.Models.DbModels;
using Benefactor.Services.Repositories;
using Bigway.Common;
using Bigway.Common.Jobs;
using SqlSugar;

namespace Benefactor.Jobs;
using static Statics;

public class CalculateAndGenerateRefererFeeJob(
    IServiceScopeFactory serviceScopeFactory,
    string systemId) : BaseBackgroundJob<CalculateAndGenerateRefererFeeJob>(serviceScopeFactory, systemId)
{
    protected override async Task DoJob(IServiceProvider serviceProvider, ILogger<CalculateAndGenerateRefererFeeJob> logger)
    {
        var refererCommissionRepository = serviceProvider.GetRequiredService<IRefererCommissionRepository>();
        var refererCommissionRecordRepository =
            serviceProvider.GetRequiredService<IRefererCommissionRecordRepository>();
        var benefitsCardOrderRepository = serviceProvider.GetRequiredService<IBenefitsCardOrderRepository>();
        
        var refererFeeUnpaidBenefitsCardOrders = await benefitsCardOrderRepository
            .GetAllAsync(query => query
                .Where(x =>
                    !x.IsDeleted &&
                    x.SystemId == SystemId &&
                    x.BenefitsCardOrderStatusCode == PaidBenefitsCardOrderStatus &&
                    x.RefererAccount != null &&
                    x.RefererFee > 0
                ));

        if (refererFeeUnpaidBenefitsCardOrders.Count == 0)
        {
            logger.LogInformation("No new benefits card orders");
            return;
        }

        logger.LogInformation("Found {Count} new benefits card orders", refererFeeUnpaidBenefitsCardOrders.Count);
        var refererAccounts = refererFeeUnpaidBenefitsCardOrders
            .Select(x => x.RefererAccount)
            .Distinct()
            .ToHashSet();

        var refererCommissions = await refererCommissionRepository.GetAllAsync(
            query =>
                query.Where(x =>
                    !x.IsDeleted &&
                    x.SystemId == SystemId &&
                    refererAccounts.Contains(x.RefererAccount)
                ).Includes(x => x.RefererCommissionRecords)
                .Includes(x => x.RefererCommissionWithdrawalRecords)
            );
        var refererCommissionsMap = refererCommissions
            .ToDictionary(x => x.RefererAccount);

        var newRefererCommissions = new List<RefererCommission>();
        var updatedRefererCommissions = new List<RefererCommission>();
        var newRefererCommissionRecords = new List<RefererCommissionRecord>();
        var now = DateTime.UtcNow;
        var newRefererCommissionsMap = new Dictionary<string, RefererCommission>();
        foreach (var benefitsCardOrder in refererFeeUnpaidBenefitsCardOrders)
        {
            var refererAccount = benefitsCardOrder.RefererAccount!;
            var refererCommission =
                refererCommissionsMap.GetValueOrDefault(refererAccount)
                ?? new RefererCommission
                {
                    RefererAccount = refererAccount,
                    SystemId = SystemId,
                    CreateTime = now,
                    RefererCommissionRecords = [],
                    RefererCommissionWithdrawalRecords = []
                };

            if (refererCommission.RefererCommissionRecords!
                .Exists(x => x.BenefitsCardOrderId == benefitsCardOrder.Id))
            {
                logger.LogWarning("The benefits order with id {BenefitsCardOrderId} already exists in the referer fee records",
                    benefitsCardOrder.Id);
                benefitsCardOrder.BenefitsCardOrderStatusCode = RefererFeePaidBenefitsCardOrderStatus;
                benefitsCardOrder.UpdateTime = now;
                continue;
            }

            var refererFee = benefitsCardOrder.RefererFee!.Value;
            if (refererCommission.RefererCommissionRecords!.Count == 0)
            {
                newRefererCommissions.Add(refererCommission);
                newRefererCommissionsMap[refererAccount] = refererCommission;
                refererCommissionsMap[refererAccount] = refererCommission;
            }
            else
            {
                if (!newRefererCommissionsMap.ContainsKey(refererAccount))
                {
                    refererCommission.UpdateTime = now;
                    updatedRefererCommissions.Add(refererCommission);
                }
            }

            var refererCommissionRecord = new RefererCommissionRecord
            {
                RefererCommissionId = refererCommission.Id,
                BenefitsCardOrderId = benefitsCardOrder.Id,
                RefererAccount = refererAccount,
                BuyerAccount = benefitsCardOrder.BuyerAccount,
                RefererFee = refererFee,
                RefererFeePaidTime = now,
                CreateTime = now,
                SystemId = SystemId
            };

            newRefererCommissionRecords.Add(refererCommissionRecord);

            refererCommission.RefererCommissionRecords.Add(refererCommissionRecord);

            benefitsCardOrder.BenefitsCardOrderStatusCode = RefererFeePaidBenefitsCardOrderStatus;
            benefitsCardOrder.UpdateTime = now;
        }

        var db = serviceProvider.GetRequiredKeyedService<ISqlSugarClient>(IocKeys.SqliteDbKey);
        var ado = db.Ado;
        try
        {
            await ado.BeginTranAsync(IsolationLevel.ReadUncommitted);

            logger.LogInformation("Updating {Count} benefits card orders", refererFeeUnpaidBenefitsCardOrders.Count);
            await benefitsCardOrderRepository.UpdateAllAsync(refererFeeUnpaidBenefitsCardOrders);

            if (newRefererCommissions.Count > 0)
            {
                logger.LogInformation("Adding {Count} new referer commissions", newRefererCommissions.Count);
                await refererCommissionRepository.AddAllAsync(newRefererCommissions);
            }

            if (newRefererCommissionRecords.Count > 0)
            {
                logger.LogInformation("Adding {Count} new referer commission records", newRefererCommissionRecords.Count);
                await refererCommissionRecordRepository.AddAllAsync(newRefererCommissionRecords);
            }

            if (updatedRefererCommissions.Count > 0)
            {
                logger.LogInformation("Updating {Count} referer commissions", updatedRefererCommissions.Count);
                await refererCommissionRepository.UpdateAllAsync(updatedRefererCommissions);
            }

            await ado.CommitTranAsync();
        }
        catch
        {
            await ado.RollbackTranAsync();
            throw;
        }
    }
}